Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
PaiwiseProcessor.cs
1using Paiwise;
2using System;
3using System.Collections.Generic;
4using System.Text;
5using System.Threading.Tasks;
6using System.Xml;
7using Waher.Content;
9using Waher.Events;
18using Waher.Script;
28
30{
34 public static class PaiwiseProcessor
35 {
39 public const string PaymentInstructionsNamespace = "https://paiwise.tagroot.io/Schema/PaymentInstructions.xsd";
40
41 private static readonly AsyncQueue<WorkItem> paymentsToProcess = new AsyncQueue<WorkItem>();
42
46 public const string TrustProviderRole = "TrustProvider";
47
58 internal static async Task ContractSigned(Contract Contract, bool ContractIsLocked,
59 Dictionary<CaseInsensitiveString, Parameter> TransientParameters,
61 {
62 if (Contract?.ForMachines is null ||
63 Contract.ForMachinesNamespace != PaymentInstructionsNamespace)
64 {
65 return;
66 }
67
68 switch (Contract.ForMachinesLocalName)
69 {
70 case "Nop":
71 break;
72
73 case "PaymentInstructions":
74 if (Contract.State != ContractState.Signed)
75 return;
76
77 XmlDocument Doc = new XmlDocument()
78 {
79 PreserveWhitespace = true
80 };
81 Doc.LoadXml(Contract.ForMachines);
82
83 foreach (XmlNode N in Doc.DocumentElement.ChildNodes)
84 {
85 if (!(N is XmlElement E) || E.NamespaceURI != PaymentInstructionsNamespace)
86 continue;
87
88 switch (E.LocalName)
89 {
90 case "Payment":
91 await AddPayment(E, Contract, EDaler);
92 break;
93 }
94 }
95 break;
96
97 case "Payment":
98 if (Contract.State != ContractState.Signed)
99 return;
100
101 Doc = new XmlDocument()
102 {
103 PreserveWhitespace = true
104 };
105 Doc.LoadXml(Contract.ForMachines);
106
107 await AddPayment(Doc.DocumentElement, Contract, EDaler);
108 break;
109
110 case "BuyEDaler":
111 if (!MarketplaceProcessor.OnlyMissingAuctioneer( // Checks that Contract.State == ContractState.BeingSigned
113 out string PaymentLegalId, out string PaymentJid))
114 {
115 return;
116 }
117
118 Doc = new XmlDocument()
119 {
120 PreserveWhitespace = true
121 };
122 Doc.LoadXml(Contract.ForMachines);
123
124 await BuyEDaler(Doc.DocumentElement, Contract, ContractIsLocked,
125 TransientParameters, Legal, EDaler, PaymentLegalId, PaymentJid);
126 break;
127
128 case "SellEDaler":
129 if (!MarketplaceProcessor.OnlyMissingAuctioneer( // Checks that Contract.State == ContractState.BeingSigned
131 out PaymentLegalId, out PaymentJid))
132 {
133 return;
134 }
135
136 Doc = new XmlDocument()
137 {
138 PreserveWhitespace = true
139 };
140 Doc.LoadXml(Contract.ForMachines);
141
142 await SellEDaler(Doc.DocumentElement, Contract, ContractIsLocked,
143 TransientParameters, Legal, EDaler, PaymentLegalId, PaymentJid);
144 break;
145
146 default:
147 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked,
148 "Unrecognized content in payments contract: " + Contract.ForMachinesLocalName,
149 true, Legal, EDaler);
150 break;
151 }
152 }
153
154 #region Buy eDaler
155
156 private static async Task BuyEDaler(XmlElement Instructions, Contract Contract, bool ContractIsLocked,
157 Dictionary<CaseInsensitiveString, Parameter> TransientParameters, LegalComponent Legal,
158 EDalerComponent EDaler, string PaymentLegalId, string PaymentJid)
159 {
160 try
161 {
162 string ServiceId = XML.Attribute(Instructions, "serviceId");
163 string ServiceProvider = XML.Attribute(Instructions, "serviceProvider");
164 decimal? Amount = null;
165 CaseInsensitiveString Currency = null;
166
167 if (string.IsNullOrEmpty(ServiceId))
168 {
169 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to buy eDaler: Service ID not defined.", true, Legal, EDaler);
170 return;
171 }
172
173 if (string.IsNullOrEmpty(ServiceProvider))
174 {
175 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to buy eDaler: Service Provider not defined.", true, Legal, EDaler);
176 return;
177 }
178
179 Type T = Types.GetType(ServiceProvider);
180 if (T is null)
181 {
182 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to buy eDaler: Service Provider " + ServiceProvider + " not found or installed.", true, Legal, EDaler);
183 return;
184 }
185
186 if (!typeof(IBuyEDalerServiceProvider).IsAssignableFrom(T) ||
188 {
189 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to buy eDaler: Service Provider does not support buying of eDaler.", true, Legal, EDaler);
190 return;
191 }
192
193 foreach (XmlNode N in Instructions.ChildNodes)
194 {
195 if (N is XmlElement E)
196 {
197 switch (E.LocalName)
198 {
199 case "Amount":
200 if (!(await GetParameterValue(E, Contract) is decimal d))
201 {
202 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to buy eDaler: Amount not defined.", true, Legal, EDaler);
203 return;
204 }
205
206 Amount = d;
207 break;
208
209 case "Currency":
210 if (!(await GetParameterValue(E, Contract) is string s))
211 {
212 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to buy eDaler: Currency not defined.", true, Legal, EDaler);
213 return;
214 }
215
216 Currency = s;
217 break;
218
219 default:
220 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to buy eDaler: Undefined elements in contract.", true, Legal, EDaler);
221 return;
222 }
223 }
224 }
225
226 if (!Amount.HasValue || string.IsNullOrEmpty(Currency))
227 {
228 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to buy eDaler: Incomplete instructions.", true, Legal, EDaler);
229 return;
230 }
231
232 if (Amount.Value <= 0)
233 {
234 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to buy eDaler: Amount must be positive.", true, Legal, EDaler);
235 return;
236 }
237
238 XmppAddress PaymentAddress = new XmppAddress(PaymentJid);
239 if (!PaymentAddress.HasAccount)
240 {
241 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to buy eDaler: Invalid buyer JID.", true, Legal, EDaler);
242 return;
243 }
244
245 if (!EDaler.Server.IsServerDomain(PaymentAddress.Domain, true))
246 {
247 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to buy eDaler: Buyer does not have an account on the current broker.", true, Legal, EDaler);
248 return;
249 }
250
251 LegalIdentity PaymentIdentity = await LegalComponent.GetLocalLegalIdentity(PaymentLegalId);
252
253 if (PaymentIdentity is null)
254 {
255 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to get buyer legal identity.", true, Legal, EDaler);
256 return;
257 }
258
259 string Country = PaymentIdentity["COUNTRY"];
260
261 if (string.IsNullOrEmpty(Country))
262 {
263 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Buyer legal identity lacks country.", true, Legal, EDaler);
264 return;
265 }
266
267 IBuyEDalerService Service = await BuyEDalerServiceProvider.GetServiceForBuyingEDaler(ServiceId, Currency, Country);
268
269 if (Service is null)
270 {
271 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Payment Service ID not found.", true, Legal, EDaler);
272 return;
273 }
274
275 if (!await Service.CanBuyEDaler(PaymentAddress.Account))
276 {
277 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to buy eDaler: Selected service provider cannot perform action.", true, Legal, EDaler);
278 return;
279 }
280
281 if (Service.Supports(Currency) == Grade.NotAtAll)
282 {
283 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to buy eDaler: Selected service provider does not support currency.", true, Legal, EDaler);
284 return;
285 }
286
287 if ((Service.BuyEDalerTemplateContractId is null || Contract.TemplateId != Service.BuyEDalerTemplateContractId) &&
288 (EDaler.Server.Domain != "example.com" ||
290 {
291 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to buy eDaler: Invalid template used.", true, Legal, EDaler);
292 return;
293 }
294
295 if (await MarketplaceProcessor.SignContract(Contract, ContractIsLocked, Legal, TrustProviderRole) is null)
296 return;
297
298 Dictionary<CaseInsensitiveString, object> ContractParameters = new Dictionary<CaseInsensitiveString, object>();
299 Dictionary<CaseInsensitiveString, CaseInsensitiveString> BuyerIdParameters = new Dictionary<CaseInsensitiveString, CaseInsensitiveString>();
300
301 foreach (Parameter P in Contract.Parameters)
302 ContractParameters[P.Name] = P.ObjectValue;
303
304 if (!(TransientParameters is null))
305 {
306 foreach (KeyValuePair<CaseInsensitiveString, Parameter> P in TransientParameters)
307 ContractParameters[P.Key] = P.Value.ObjectValue;
308 }
309
310 foreach (Property P in PaymentIdentity.Properties)
311 BuyerIdParameters[P.Name] = P.Value;
312
313 BuyEDaler(Contract, ContractIsLocked, Amount.Value, Currency, Service, Legal, EDaler,
314 PaymentLegalId, Contract.ContractId, ContractParameters, BuyerIdParameters);
315 }
316 catch (Exception ex)
317 {
318 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, ex.Message, true, Legal, EDaler);
319 }
320 }
321
322 private static async void BuyEDaler(Contract Contract, bool ContractIsLocked,
323 decimal Amount, string Currency, IBuyEDalerService BuyEDalerService,
324 LegalComponent Legal, EDalerComponent EDaler, string PaymentLegalId,
325 string ContractId, Dictionary<CaseInsensitiveString, object> ContractParameters,
326 Dictionary<CaseInsensitiveString, CaseInsensitiveString> BuyerIdParameters)
327 {
328 try
329 {
330 if (!string.IsNullOrEmpty(ContractId) && !ContractParameters.ContainsKey(nameof(ContractId)))
331 ContractParameters[nameof(ContractId)] = ContractId;
332
333 PaymentResult PaymentResult = await BuyEDalerService.BuyEDaler(ContractParameters,
334 BuyerIdParameters, Amount, Currency, null, null, null, async (Sender, e) =>
335 {
336 if (BuyerIdParameters.TryGetValue("JID", out CaseInsensitiveString JID))
337 {
338 StringBuilder Xml = new StringBuilder();
339
340 Xml.Append("<buyEDalerClientUrl xmlns='");
341 Xml.Append(EDalerComponent.NamespaceEDaler);
342 Xml.Append("' tid='");
343 Xml.Append(XML.Encode(Contract.ContractId));
344 Xml.Append("' url='");
345 Xml.Append(XML.Encode(e.Url));
346 Xml.Append("'/>");
347
348 await EDaler.Server.SendMessage(string.Empty, string.Empty, EDaler.MainDomain,
349 new XmppAddress(JID), string.Empty, Xml.ToString());
350 }
351 }, null);
352
353 if (!PaymentResult.Ok)
354 {
355 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to buy eDaler: " + PaymentResult.Error, true, Legal, EDaler);
356 return;
357 }
358
359 StringBuilder Uri = new StringBuilder();
360 DateTime Expires = Contract.Duration.HasValue ? (DateTime.Today + Contract.Duration.Value) : DateTime.Today.AddDays(365);
361 Guid Id = Guid.NewGuid();
362 DateTime Created = DateTime.UtcNow;
363
364 Uri.Append("edaler:is=");
365 Uri.Append(EDaler.Server.Domain);
366 Uri.Append(";ti=");
367 Uri.Append(PaymentLegalId);
368 Uri.Append(";id=");
369 Uri.Append(Id.ToString());
370 Uri.Append(";cr=");
371 Uri.Append(XML.Encode(Created, false));
372 Uri.Append(";am=");
373 Uri.Append(CommonTypes.Encode(Amount));
374 Uri.Append(";cu=");
375 Uri.Append(Currency);
376 Uri.Append(";ex=");
377 Uri.Append(XML.Encode(Expires, true));
378 Uri.Append(";m=");
379 Uri.Append(Convert.ToBase64String(Encoding.UTF8.GetBytes("iotsc:" + Contract.ContractId)));
380
381 SignUri(Uri);
382
383 string Msg = await ProcessPayment(Uri.ToString(), EDaler);
384 if (!string.IsNullOrEmpty(Msg))
385 {
386 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to buy eDaler: " + Msg, false, Legal, EDaler);
387 return;
388 }
389 }
390 catch (Exception ex)
391 {
392 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, ex.Message, true, Legal, EDaler);
393 }
394 }
395
396 internal static async Task<PaymentResult> BuyEDaler(decimal Amount, string Currency, string SuccessUrl, string FailureUrl, string CancelUrl,
397 IBuyEDalerService BuyEDalerService, EDalerComponent EDaler, string PaymentLegalId, string ContractId,
398 Dictionary<CaseInsensitiveString, object> ContractParameters, Dictionary<CaseInsensitiveString, CaseInsensitiveString> BuyerIdParameters,
399 string Reference, ClientUrlEventHandler ClientUrlCallback, object State)
400 {
401 try
402 {
403 if (!string.IsNullOrEmpty(ContractId) && !ContractParameters.ContainsKey(nameof(ContractId)))
404 ContractParameters[nameof(ContractId)] = ContractId;
405
406 PaymentResult PaymentResult = await BuyEDalerService.BuyEDaler(ContractParameters, BuyerIdParameters,
407 Amount, Currency, SuccessUrl, FailureUrl, CancelUrl, ClientUrlCallback, State);
408
409 if (!PaymentResult.Ok)
410 return new PaymentResult("Unable to buy eDaler: " + PaymentResult.Error);
411
412 StringBuilder Uri = new StringBuilder();
413 DateTime Expires = DateTime.Today.AddDays(365);
414 Guid Id = Guid.NewGuid();
415 DateTime Created = DateTime.UtcNow;
416
417 Uri.Append("edaler:is=");
418 Uri.Append(EDaler.Server.Domain);
419 Uri.Append(";ti=");
420 Uri.Append(PaymentLegalId);
421 Uri.Append(";id=");
422 Uri.Append(Id.ToString());
423 Uri.Append(";cr=");
424 Uri.Append(XML.Encode(Created, false));
425 Uri.Append(";am=");
427 Uri.Append(";cu=");
428 Uri.Append(PaymentResult.Currency);
429 Uri.Append(";ex=");
430 Uri.Append(XML.Encode(Expires, true));
431
432 if (!string.IsNullOrEmpty(Reference))
433 {
434 Uri.Append(";m=");
435 Uri.Append(Convert.ToBase64String(Encoding.UTF8.GetBytes(Reference)));
436 }
437
438 SignUri(Uri);
439
440 string Msg = await ProcessPayment(Uri.ToString(), EDaler);
441 if (!string.IsNullOrEmpty(Msg))
442 return new PaymentResult("Unable to buy eDaler: " + Msg);
443
445 }
446 catch (Exception ex)
447 {
448 return new PaymentResult(ex.Message);
449 }
450 }
451
452 #endregion
453
454 #region Sell eDaler
455
456 private static async Task SellEDaler(XmlElement Instructions, Contract Contract, bool ContractIsLocked,
457 Dictionary<CaseInsensitiveString, Parameter> TransientParameters, LegalComponent Legal,
458 EDalerComponent EDaler, string PaymentLegalId, string PaymentJid)
459 {
460 try
461 {
462 string ServiceId = XML.Attribute(Instructions, "serviceId");
463 string ServiceProvider = XML.Attribute(Instructions, "serviceProvider");
464 decimal? Amount = null;
465 CaseInsensitiveString Currency = null;
466
467 if (string.IsNullOrEmpty(ServiceId))
468 {
469 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to sell eDaler: Service ID not defined.", true, Legal, EDaler);
470 return;
471 }
472
473 if (string.IsNullOrEmpty(ServiceProvider))
474 {
475 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to sell eDaler: Service Provider not defined.", true, Legal, EDaler);
476 return;
477 }
478
479 Type T = Types.GetType(ServiceProvider);
480 if (T is null)
481 {
482 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to sell eDaler: Service Provider " + ServiceProvider + " not found or installed.", true, Legal, EDaler);
483 return;
484 }
485
486 if (!typeof(ISellEDalerServiceProvider).IsAssignableFrom(T) ||
488 {
489 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to sell eDaler: Service Provider does not support selling of eDaler.", true, Legal, EDaler);
490 return;
491 }
492
493 foreach (XmlNode N in Instructions.ChildNodes)
494 {
495 if (N is XmlElement E)
496 {
497 switch (E.LocalName)
498 {
499 case "Amount":
500 if (!(await GetParameterValue(E, Contract) is decimal d))
501 {
502 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to sell eDaler: Amount not defined.", true, Legal, EDaler);
503 return;
504 }
505
506 Amount = d;
507 break;
508
509 case "Currency":
510 if (!(await GetParameterValue(E, Contract) is string s))
511 {
512 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to sell eDaler: Currency not defined.", true, Legal, EDaler);
513 return;
514 }
515
516 Currency = s;
517 break;
518
519 default:
520 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to sell eDaler: Undefined elements in contract.", true, Legal, EDaler);
521 return;
522 }
523 }
524 }
525
526 if (!Amount.HasValue || string.IsNullOrEmpty(Currency))
527 {
528 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to sell eDaler: Incomplete instructions.", true, Legal, EDaler);
529 return;
530 }
531
532 if (Amount.Value <= 0)
533 {
534 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to sell eDaler: Amount must be positive.", true, Legal, EDaler);
535 return;
536 }
537
538 XmppAddress PaymentAddress = new XmppAddress(PaymentJid);
539 if (!PaymentAddress.HasAccount)
540 {
541 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to sell eDaler: Invalid seller JID.", true, Legal, EDaler);
542 return;
543 }
544
545 if (!EDaler.Server.IsServerDomain(PaymentAddress.Domain, true))
546 {
547 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to sell eDaler: Seller does not have an account on the current broker.", true, Legal, EDaler);
548 return;
549 }
550
551 LegalIdentity PaymentIdentity = await LegalComponent.GetLocalLegalIdentity(PaymentLegalId);
552
553 if (PaymentIdentity is null)
554 {
555 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to get seller legal identity.", true, Legal, EDaler);
556 return;
557 }
558
559 string Country = PaymentIdentity["COUNTRY"];
560
561 if (string.IsNullOrEmpty(Country))
562 {
563 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Seller legal identity lacks country.", true, Legal, EDaler);
564 return;
565 }
566
567 ISellEDalerService Service = await SellEDalerServiceProvider.GetServiceForSellingEDaler(ServiceId, Currency, Country);
568
569 if (Service is null)
570 {
571 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Payment Service ID not found.", true, Legal, EDaler);
572 return;
573 }
574
575 if (!await Service.CanSellEDaler(PaymentAddress.Account))
576 {
577 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to sell eDaler: Selected service provider cannot perform action.", true, Legal, EDaler);
578 return;
579 }
580
581 if (Service.Supports(Currency) == Grade.NotAtAll)
582 {
583 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to sell eDaler: Selected service provider does not support currency.", true, Legal, EDaler);
584 return;
585 }
586
587 if ((Service.SellEDalerTemplateContractId is null || Contract.TemplateId != Service.SellEDalerTemplateContractId) &&
588 (EDaler.Server.Domain != "example.com" ||
590 {
591 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to sell eDaler: Invalid template used.", true, Legal, EDaler);
592 return;
593 }
594
595 if (await MarketplaceProcessor.SignContract(Contract, ContractIsLocked, Legal, TrustProviderRole) is null)
596 return;
597
598 Dictionary<CaseInsensitiveString, object> ContractParameters = new Dictionary<CaseInsensitiveString, object>();
599 Dictionary<CaseInsensitiveString, CaseInsensitiveString> SellerIdParameters = new Dictionary<CaseInsensitiveString, CaseInsensitiveString>();
600
601 foreach (Parameter P in Contract.Parameters)
602 ContractParameters[P.Name] = P.ObjectValue;
603
604 if (!(TransientParameters is null))
605 {
606 foreach (KeyValuePair<CaseInsensitiveString, Parameter> P in TransientParameters)
607 ContractParameters[P.Key] = P.Value.ObjectValue;
608 }
609
610 foreach (Property P in PaymentIdentity.Properties)
611 SellerIdParameters[P.Name] = P.Value;
612
613 SellEDaler(Contract, ContractIsLocked, Amount.Value, Currency, Service, Legal, EDaler,
614 PaymentLegalId, Contract.ContractId, ContractParameters, SellerIdParameters);
615 }
616 catch (Exception ex)
617 {
618 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, ex.Message, true, Legal, EDaler);
619 }
620 }
621
622 internal static async void SellEDaler(Contract Contract, bool ContractIsLocked, decimal Amount, string Currency,
624 string PaymentLegalId, string ContractId, Dictionary<CaseInsensitiveString, object> ContractParameters,
625 Dictionary<CaseInsensitiveString, CaseInsensitiveString> SellerIdParameters)
626 {
627 try
628 {
629 if (!string.IsNullOrEmpty(ContractId) && !ContractParameters.ContainsKey(nameof(ContractId)))
630 ContractParameters[nameof(ContractId)] = ContractId;
631
632 StringBuilder Uri = new StringBuilder();
633 DateTime Expires = Contract.Duration.HasValue ? (DateTime.Today + Contract.Duration.Value) : DateTime.Today.AddDays(365);
634 Guid Id = Guid.NewGuid();
635 DateTime Created = DateTime.UtcNow;
636
637 Uri.Append("edaler:xx=");
638 Uri.Append(EDaler.Server.Domain);
639 Uri.Append(";fi=");
640 Uri.Append(PaymentLegalId);
641 Uri.Append(";id=");
642 Uri.Append(Id.ToString());
643 Uri.Append(";cr=");
644 Uri.Append(XML.Encode(Created, false));
645 Uri.Append(";am=");
646 Uri.Append(CommonTypes.Encode(Amount));
647 Uri.Append(";cu=");
648 Uri.Append(Currency);
649 Uri.Append(";ex=");
650 Uri.Append(XML.Encode(Expires, true));
651 Uri.Append(";m=");
652 Uri.Append(Convert.ToBase64String(Encoding.UTF8.GetBytes("iotsc:" + Contract.ContractId)));
653 Uri.Append(";cs=");
654 Uri.Append(Contract.ContractId);
655
656 SignUri(Uri);
657
658 string Msg = await ProcessPayment(Uri.ToString(), EDaler);
659 if (!string.IsNullOrEmpty(Msg))
660 {
661 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to sell eDaler: " + Msg, true, Legal, EDaler);
662 return;
663 }
664
665 PaymentResult PaymentResult = await SellEDalerService.SellEDaler(ContractParameters,
666 SellerIdParameters, Amount, Currency, null, null, null, null, null);
667
668 if (!PaymentResult.Ok)
669 {
670 Uri.Clear();
671 Id = Guid.NewGuid();
672 Created = DateTime.UtcNow;
673
674 Uri.Append("edaler:is=");
675 Uri.Append(EDaler.Server.Domain);
676 Uri.Append(";ti=");
677 Uri.Append(PaymentLegalId);
678 Uri.Append(";id=");
679 Uri.Append(Id.ToString());
680 Uri.Append(";cr=");
681 Uri.Append(XML.Encode(Created, false));
682 Uri.Append(";am=");
683 Uri.Append(CommonTypes.Encode(Amount));
684 Uri.Append(";cu=");
685 Uri.Append(Currency);
686 Uri.Append(";ex=");
687 Uri.Append(XML.Encode(Expires, true));
688 Uri.Append(";m=");
689 Uri.Append(Convert.ToBase64String(Encoding.UTF8.GetBytes("iotsc:" + Contract.ContractId)));
690
691 SignUri(Uri);
692
693 Msg = await ProcessPayment(Uri.ToString(), EDaler);
694 if (string.IsNullOrEmpty(Msg))
695 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, "Unable to sell eDaler: " + PaymentResult.Error, true, Legal, EDaler);
696 else
697 {
698 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked,
699 "Unable to sell eDaler: " + PaymentResult.Error +
700 ". Furthermore, unable to re-issue eDaler: " + Msg +
701 ". Contact operator.", true, Legal, EDaler);
702 }
703
704 return;
705 }
706 }
707 catch (Exception ex)
708 {
709 await NeuroFeaturesProcessor.RejectContract(Contract, ContractIsLocked, ex.Message, true, Legal, EDaler);
710 }
711 }
712
713 internal static async Task<PaymentResult> SellEDaler(decimal Amount, string Currency, string SuccessUrl, string FailureUrl, string CancelUrl,
714 ISellEDalerService SellEDalerService, EDalerComponent EDaler, string PaymentLegalId,
715 string SignaturesContractId, Dictionary<CaseInsensitiveString, object> ContractParameters,
716 Dictionary<CaseInsensitiveString, CaseInsensitiveString> SellerIdParameters,
717 string Reference, ClientUrlEventHandler ClientUrlCallback, object State)
718 {
719 try
720 {
721 if (!string.IsNullOrEmpty(SignaturesContractId) && !ContractParameters.ContainsKey(nameof(SignaturesContractId)))
722 ContractParameters[nameof(SignaturesContractId)] = SignaturesContractId;
723
724 StringBuilder Uri = new StringBuilder();
725 DateTime Expires = DateTime.Today.AddDays(365);
726 Guid Id = Guid.NewGuid();
727 DateTime Created = DateTime.UtcNow;
728
729 Uri.Append("edaler:xx=");
730 Uri.Append(EDaler.Server.Domain);
731 Uri.Append(";fi=");
732 Uri.Append(PaymentLegalId);
733 Uri.Append(";id=");
734 Uri.Append(Id.ToString());
735 Uri.Append(";cr=");
736 Uri.Append(XML.Encode(Created, false));
737 Uri.Append(";am=");
738 Uri.Append(CommonTypes.Encode(Amount));
739 Uri.Append(";cu=");
740 Uri.Append(Currency);
741 Uri.Append(";ex=");
742 Uri.Append(XML.Encode(Expires, true));
743
744 if (!string.IsNullOrEmpty(Reference))
745 {
746 Uri.Append(";m=");
747 Uri.Append(Convert.ToBase64String(Encoding.UTF8.GetBytes(Reference)));
748 }
749
750 if (!string.IsNullOrEmpty(SignaturesContractId))
751 {
752 Uri.Append(";cs=");
753 Uri.Append(SignaturesContractId);
754 }
755
756 SignUri(Uri);
757
758 string Msg = await ProcessPayment(Uri.ToString(), EDaler);
759 if (!string.IsNullOrEmpty(Msg))
760 return new PaymentResult("Unable to sell eDaler: " + Msg);
761
762 PaymentResult PaymentResult = await SellEDalerService.SellEDaler(ContractParameters, SellerIdParameters, Amount, Currency,
763 SuccessUrl, FailureUrl, CancelUrl, ClientUrlCallback, State);
764
765 if (!PaymentResult.Ok)
766 {
767 Uri.Clear();
768 Id = Guid.NewGuid();
769 Created = DateTime.UtcNow;
770
771 Uri.Append("edaler:is=");
772 Uri.Append(EDaler.Server.Domain);
773 Uri.Append(";ti=");
774 Uri.Append(PaymentLegalId);
775 Uri.Append(";id=");
776 Uri.Append(Id.ToString());
777 Uri.Append(";cr=");
778 Uri.Append(XML.Encode(Created, false));
779 Uri.Append(";am=");
780 Uri.Append(CommonTypes.Encode(Amount));
781 Uri.Append(";cu=");
782 Uri.Append(Currency);
783 Uri.Append(";ex=");
784 Uri.Append(XML.Encode(Expires, true));
785 Uri.Append(";m=");
786 Uri.Append(Convert.ToBase64String(Encoding.UTF8.GetBytes(Reference)));
787
788 SignUri(Uri);
789
790 Msg = await ProcessPayment(Uri.ToString(), EDaler);
791 if (string.IsNullOrEmpty(Msg))
792 return new PaymentResult("Unable to sell eDaler: " + PaymentResult.Error);
793 else
794 {
795 return new PaymentResult("Unable to sell eDaler: " + PaymentResult.Error +
796 ". Furthermore, unable to re-issue eDaler: " + Msg +
797 ". Contact operator.");
798 }
799 }
800
802 }
803 catch (Exception ex)
804 {
805 return new PaymentResult(ex.Message);
806 }
807 }
808
809 #endregion
810
811 private static async Task AddPayment(XmlElement Payment, Contract Contract, EDalerComponent EDaler)
812 {
813 Guid Id = Guid.NewGuid();
814 CaseInsensitiveString FromLegalId = null;
815 CaseInsensitiveString ToLegalId = null;
816 string Currency = null;
817 string Reference = null;
818 CaseInsensitiveString ConditionContractId = null;
819 decimal? Amount = null;
820 decimal? AmountExtra = null;
821 double? ValidDays = null;
822
823 foreach (XmlNode N in Payment.ChildNodes)
824 {
825 if (!(N is XmlElement E) || E.NamespaceURI != PaymentInstructionsNamespace)
826 continue;
827
828 switch (E.LocalName)
829 {
830 case "From":
831 FromLegalId = GetLegalId(E, Contract);
832 if (FromLegalId is null)
833 {
834 Log.Error("Unable to add payment defined in Payment Instructions: Sender identity not defined.", Contract.ContractId);
835 return;
836 }
837 break;
838
839 case "To":
840 ToLegalId = GetLegalId(E, Contract);
841 if (ToLegalId is null)
842 {
843 Log.Error("Unable to add payment defined in Payment Instructions: Recipient identity not defined.", Contract.ContractId);
844 return;
845 }
846 break;
847
848 case "Amount":
849 if (!(await GetParameterValue(E, Contract) is decimal d))
850 {
851 Log.Error("Unable to add payment defined in Payment Instructions: Amount not defined.", Contract.ContractId);
852 return;
853 }
854
855 Amount = d;
856 break;
857
858
859 case "AmountExtra":
860 if (!(await GetParameterValue(E, Contract) is decimal d2))
861 {
862 Log.Error("Unable to add payment defined in Payment Instructions: Extra amount not defined properly.", Contract.ContractId);
863 return;
864 }
865
866 AmountExtra = d2;
867 break;
868
869 case "Currency":
870 if (!(await GetParameterValue(E, Contract) is string s))
871 {
872 Log.Error("Unable to add payment defined in Payment Instructions: Currency not defined.", Contract.ContractId);
873 return;
874 }
875
876 Currency = s;
877 break;
878
879 case "ValidDays":
880 if (!(await GetParameterValue(E, Contract) is decimal d3))
881 {
882 Log.Error("Unable to add payment defined in Payment Instructions: Validity days not defined.", Contract.ContractId);
883 return;
884 }
885
886 ValidDays = (double)d3;
887 break;
888
889 case "Reference":
890 if (!(await GetParameterValue(E, Contract) is string s2))
891 {
892 Log.Error("Unable to add payment defined in Payment Instructions: Reference not defined.", Contract.ContractId);
893 return;
894 }
895
896 Reference = s2;
897 break;
898
899 case "Condition":
900 if (!(await GetParameterValue(E, Contract) is string s3))
901 {
902 Log.Error("Unable to add payment defined in Payment Instructions: Condition not defined.", Contract.ContractId);
903 return;
904 }
905
906 int i = s3.IndexOf('@');
907 if (i < 0 || !Guid.TryParse(s3.Substring(0, i), out Guid _))
908 {
909 Log.Error("Unable to add payment defined in Payment Instructions: Invalid condition reference.", Contract.ContractId);
910 return;
911 }
912
913 ConditionContractId = s3;
914 break;
915 }
916 }
917
918 if (FromLegalId is null || ToLegalId is null || Currency is null || !Amount.HasValue || !ValidDays.HasValue || Reference is null)
919 {
920 Log.Error("Unable to add payment defined in Payment Instructions: Incomplete instructions.", Contract.ContractId);
921 return;
922 }
923
924 string Uri = GenerateContractualPaymentUri(Id, FromLegalId, true, ToLegalId, true, Currency, Amount.Value, AmountExtra, Reference,
925 Contract.ContractId, ConditionContractId, ValidDays.Value, out DateTime Created, out DateTime Expires);
926
927 Payment PaymentInstance = new Payment()
928 {
929 PaymentId = Id,
930 ContractId = Contract.ContractId,
931 FromLegalId = FromLegalId,
932 ToLegalId = ToLegalId,
933 Amount = Amount.Value,
934 AmountExtra = AmountExtra,
935 Currency = Currency,
936 ConditionContractId = ConditionContractId,
937 Created = Created,
938 Expires = Expires,
939 Reference = Reference,
940 Uri = Uri
941 };
942
943 await Database.Insert(PaymentInstance);
944 QueueForProcessing(PaymentInstance, EDaler);
945 }
946
947 internal static string GenerateContractualPaymentUri(Guid Id, string From, bool FromIsLegalId, string To, bool ToIsLegalId, string Currency,
948 decimal Amount, decimal? AmountExtra, string Reference, string ContractId, string ConditionContractId, double ValidDays,
949 out DateTime Created, out DateTime Expires)
950 {
951 StringBuilder Uri = new StringBuilder();
952
953 Created = DateTime.UtcNow;
954 Expires = Created.AddDays(ValidDays);
955
956 Uri.Append("edaler:id=");
957 Uri.Append(Id.ToString());
958
959 if (FromIsLegalId)
960 Uri.Append(";fi=");
961 else
962 Uri.Append(";f=");
963
964 Uri.Append(From);
965
966 if (ToIsLegalId)
967 Uri.Append(";ti=");
968 else
969 Uri.Append(";t=");
970
971 Uri.Append(To);
972 Uri.Append(";cu=");
973 Uri.Append(Currency);
974 Uri.Append(";am=");
975 Uri.Append(CommonTypes.Encode(Amount));
976
977 if (AmountExtra.HasValue)
978 {
979 Uri.Append(";amx=");
980 Uri.Append(CommonTypes.Encode(AmountExtra.Value));
981 }
982
983 Uri.Append(";cr=");
984 Uri.Append(XML.Encode(Created));
985 Uri.Append(";ex=");
986 Uri.Append(XML.Encode(Expires));
987 Uri.Append(";m=");
988 Uri.Append(Convert.ToBase64String(Encoding.UTF8.GetBytes(Reference)));
989 Uri.Append(";cs=");
990 Uri.Append(ContractId);
991
992 if (!string.IsNullOrEmpty(ConditionContractId))
993 {
994 Uri.Append(";c=");
995 Uri.Append(ConditionContractId);
996 }
997
998 SignUri(Uri);
999
1000 return Uri.ToString();
1001 }
1002
1003 internal static string GenerateReserveAmountUri(Guid Id, string From, bool FromIsLegalId, string Currency,
1004 decimal Amount, string Reference, string ContractId, double ValidDays, out DateTime Created, out DateTime Expires)
1005 {
1006 StringBuilder Uri = new StringBuilder();
1007
1008 Created = DateTime.UtcNow;
1009 Expires = Created.AddDays(ValidDays);
1010
1011 Uri.Append("edaler:id=");
1012 Uri.Append(Id.ToString());
1013
1014 if (FromIsLegalId)
1015 Uri.Append(";fi=");
1016 else
1017 Uri.Append(";f=");
1018
1019 Uri.Append(From);
1020 Uri.Append(";cu=");
1021 Uri.Append(Currency);
1022 Uri.Append(";pa=");
1023 Uri.Append(CommonTypes.Encode(Amount));
1024 Uri.Append(";cr=");
1025 Uri.Append(XML.Encode(Created));
1026 Uri.Append(";ex=");
1027 Uri.Append(XML.Encode(Expires));
1028 Uri.Append(";m=");
1029 Uri.Append(Convert.ToBase64String(Encoding.UTF8.GetBytes(Reference)));
1030
1031 if (!string.IsNullOrEmpty(ContractId))
1032 {
1033 Uri.Append(";cs=");
1034 Uri.Append(ContractId);
1035 }
1036
1037 SignUri(Uri);
1038
1039 return Uri.ToString();
1040 }
1041
1042 private static void SignUri(StringBuilder Uri)
1043 {
1044 byte[] PreSign = Encoding.UTF8.GetBytes(Uri.ToString());
1045 byte[] S = LedgerConfiguration.Sign(PreSign);
1046
1047 Uri.Append(";s=");
1048 Uri.Append(Convert.ToBase64String(S));
1049 }
1050
1051 internal static string GenerateReleaseAmountUri(Guid Id, string To, bool ToIsLegalId, string Currency,
1052 decimal Amount, string Reference, string ContractId, double ValidDays, out DateTime Created, out DateTime Expires)
1053 {
1054 StringBuilder Uri = new StringBuilder();
1055
1056 Created = DateTime.UtcNow;
1057 Expires = Created.AddDays(ValidDays);
1058
1059 Uri.Append("edaler:id=");
1060 Uri.Append(Id.ToString());
1061
1062 if (ToIsLegalId)
1063 Uri.Append(";ti=");
1064 else
1065 Uri.Append(";t=");
1066
1067 Uri.Append(To);
1068 Uri.Append(";cu=");
1069 Uri.Append(Currency);
1070 Uri.Append(";ra=");
1071 Uri.Append(CommonTypes.Encode(Amount));
1072 Uri.Append(";cr=");
1073 Uri.Append(XML.Encode(Created));
1074 Uri.Append(";ex=");
1075 Uri.Append(XML.Encode(Expires));
1076 Uri.Append(";m=");
1077 Uri.Append(Convert.ToBase64String(Encoding.UTF8.GetBytes(Reference)));
1078
1079 if (!string.IsNullOrEmpty(ContractId))
1080 {
1081 Uri.Append(";cs=");
1082 Uri.Append(ContractId);
1083 }
1084
1085 SignUri(Uri);
1086
1087 return Uri.ToString();
1088 }
1089
1090 internal static CaseInsensitiveString GetLegalId(XmlElement Part, Contract Contract)
1091 {
1092 return GetLegalId(Part, Contract, out _);
1093 }
1094
1095 internal static CaseInsensitiveString GetLegalId(XmlElement Part, Contract Contract, out string Role)
1096 {
1097 Role = null;
1098
1099 foreach (XmlNode N in Part.ChildNodes)
1100 {
1101 if (!(N is XmlElement E))
1102 continue;
1103
1104 switch (E.LocalName)
1105 {
1106 case "RoleReference":
1107 if (Contract.ClientSignatures is null)
1108 return null;
1109
1110 Role = XML.Attribute(E, "role");
1111 if (string.IsNullOrEmpty(Role))
1112 return null;
1113
1114 string Result = null;
1115
1116 foreach (ClientSignature Signature in Contract.ClientSignatures)
1117 {
1118 if (string.Compare(Signature.Role, Role, true) == 0)
1119 {
1120 if (Result is null)
1121 Result = Signature.LegalId;
1122 else
1123 return null;
1124 }
1125 }
1126
1127 return Result;
1128 }
1129 }
1130
1131 return null;
1132 }
1133
1134 internal static CaseInsensitiveString[] GetLegalIds(XmlElement Part, Contract Contract)
1135 {
1136 return GetLegalIds(Part, Contract, out _);
1137 }
1138
1139 internal static CaseInsensitiveString[] GetLegalIds(XmlElement Part, Contract Contract, out string Role)
1140 {
1141 List<CaseInsensitiveString> Result = null;
1142 Role = null;
1143
1144 foreach (XmlNode N in Part.ChildNodes)
1145 {
1146 if (!(N is XmlElement E))
1147 continue;
1148
1149 switch (E.LocalName)
1150 {
1151 case "RoleReference":
1152 if (Contract.ClientSignatures is null)
1153 return null;
1154
1155 Role = XML.Attribute(E, "role");
1156 if (string.IsNullOrEmpty(Role))
1157 return null;
1158
1159 foreach (ClientSignature Signature in Contract.ClientSignatures)
1160 {
1161 if (string.Compare(Signature.Role, Role, true) == 0)
1162 {
1163 if (Result is null)
1164 Result = new List<CaseInsensitiveString>();
1165
1166 Result.Add(Signature.LegalId);
1167 }
1168 }
1169 break;
1170 }
1171 }
1172
1173 return Result?.ToArray();
1174 }
1175
1176 internal static async Task<object> GetParameterValue(XmlElement Value, Contract Contract)
1177 {
1178 foreach (XmlNode N in Value.ChildNodes)
1179 {
1180 if (!(N is XmlElement E))
1181 continue;
1182
1183 switch (E.LocalName)
1184 {
1185 case "ParameterReference":
1186 string Parameter = XML.Attribute(E, "parameter");
1187 if (string.IsNullOrEmpty(Parameter))
1188 return null;
1189
1190 return Contract[Parameter];
1191
1192 case "RoleReference":
1193 string Role = XML.Attribute(E, "role");
1194 if (string.IsNullOrEmpty(Role))
1195 return null;
1196
1197 if (Contract.ClientSignatures is null)
1198 return null;
1199
1200 foreach (ClientSignature Signature in Contract.ClientSignatures)
1201 {
1202 if (Signature.Role == Role)
1203 return Signature.LegalId.Value;
1204 }
1205
1206 return null;
1207
1208 case "Expression":
1209 try
1210 {
1211 Expression Exp = new Expression(E.InnerText);
1213
1214 if (!XmppServer.CheckExpressionSafe(Exp, true, true, false, out _))
1215 return null;
1216
1217 foreach (Parameter P in Contract.Parameters)
1218 v[P.Name] = P.ObjectValue;
1219
1220 return await Exp.EvaluateAsync(v);
1221 }
1222 catch (Exception)
1223 {
1224 return null;
1225 }
1226
1227 case "ContractID":
1228 return Contract.ContractId.Value;
1229
1230 case "String":
1231 case "Uri":
1232 return E.InnerText;
1233
1234 case "Number":
1235 if (CommonTypes.TryParse(E.InnerText, out decimal d))
1236 return d;
1237 else
1238 return null;
1239
1240 case "Boolean":
1241 if (CommonTypes.TryParse(E.InnerText, out bool b))
1242 return b;
1243 else
1244 return null;
1245
1246 case "Binary":
1247 return Convert.FromBase64String(E.InnerText);
1248
1249 case "Date":
1250 if (XML.TryParse(E.InnerText, out DateTime TP))
1251 return TP.Date;
1252 else
1253 return null;
1254
1255 case "DateTime":
1256 if (XML.TryParse(E.InnerText, out TP))
1257 return TP;
1258 else
1259 return null;
1260
1261 case "Time":
1262 if (TimeSpan.TryParse(E.InnerText, out TimeSpan TS))
1263 return TS;
1264 else
1265 return null;
1266 }
1267 }
1268
1269 return null;
1270 }
1271
1272 internal static async Task QueueUnprocessedPayments(EDalerComponent EDaler)
1273 {
1274 foreach (Payment Payment in await Database.Find<Payment>(new FilterFieldEqualTo("Processed", null), "Created"))
1275 QueueForProcessing(Payment, EDaler);
1276 }
1277
1278 internal static async Task StopProcessingPayments()
1279 {
1280 lock (synchObj)
1281 {
1282 if (!started)
1283 return;
1284
1285 started = false;
1286 }
1287
1288 await paymentsToProcess.Terminate();
1289 }
1290
1291 internal static void QueueForProcessing(Payment Payment, EDalerComponent EDaler)
1292 {
1293 QueueForProcessing(new WorkItem()
1294 {
1295 Payment = Payment,
1296 EDaler = EDaler
1297 });
1298 }
1299
1300 private static void QueueForProcessing(WorkItem Item)
1301 {
1302 paymentsToProcess.Add(Item);
1303
1304 lock (synchObj)
1305 {
1306 if (!started)
1307 {
1308 Task _ = Task.Run(() => ProcessingTask());
1309 started = true;
1310 }
1311 }
1312 }
1313
1314 private class WorkItem
1315 {
1316 public Payment Payment;
1317 public EDalerComponent EDaler;
1318 }
1319
1320 private static readonly object synchObj = new object();
1321 private static bool started = false;
1322
1323 private static async Task ProcessingTask()
1324 {
1325 try
1326 {
1327 WorkItem Item;
1328 Payment Payment;
1329
1330 while (!((Item = await paymentsToProcess.Wait()) is null))
1331 {
1332 Payment = Item.Payment;
1333 if (Payment.Processed.HasValue)
1334 continue;
1335
1336 try
1337 {
1338 DateTime Now = DateTime.UtcNow;
1339
1340 if (Payment.Expires.ToUniversalTime() <= Now)
1341 {
1342 Payment.Processed = Now;
1343
1344 if (Payment.LastError is null)
1345 {
1346 Payment.LastError = "Expired";
1347 Payment.NrErrors++;
1348 }
1349 }
1350 else
1351 {
1352 string ErrorMessage = await ProcessPayment(Payment.Uri, Item.EDaler);
1353
1354 if (string.IsNullOrEmpty(ErrorMessage))
1355 {
1356 Payment.Processed = Now;
1357 Payment.LastError = null;
1358 }
1359 else
1360 {
1361 Payment.LastError = ErrorMessage;
1362 Payment.NrErrors++;
1363
1364 Gateway.ScheduleEvent(RetryPayment, DateTime.Now.AddHours(1), Item);
1365 }
1366 }
1367
1368 await Database.Update(Payment);
1369 }
1370 catch (Exception ex)
1371 {
1372 Log.Exception(ex,
1373 new KeyValuePair<string, object>("PaymentId", Payment.PaymentId.ToString()),
1374 new KeyValuePair<string, object>("ContractId", Payment.ContractId));
1375 }
1376 }
1377 }
1378 catch (Exception ex)
1379 {
1380 Log.Exception(ex);
1381 }
1382 finally
1383 {
1384 started = false;
1385 }
1386 }
1387
1388 private static void RetryPayment(object State)
1389 {
1390 QueueForProcessing((WorkItem)State);
1391 }
1392
1399 internal static async Task<string> ProcessPayment(string PaymentUri, EDalerComponent EDaler)
1400 {
1401 EDalerUriState State = new InternalProcessing(PaymentUri);
1402 EDalerUri Uri = await EDalerUri.Parse(PaymentUri, State, EDaler);
1403 if (Uri is null)
1404 return "Unable to parse payment URI: " + PaymentUri;
1405
1406 ITransaction UriTransaction;
1407 List<ITransaction> Parts = new List<ITransaction>();
1408 Uri.AddTransactionParts(Parts, false, EDaler.Legal);
1409
1410 if (Parts.Count == 1)
1411 UriTransaction = Parts[0];
1412 else
1413 UriTransaction = new CompositeTransaction(Uri.Id, true, Parts.ToArray());
1414
1415 try
1416 {
1417 if (!await UriTransaction.Prepare())
1418 {
1419 Uri.State.Error(EDalerUriErrorType.BadRequest, "Unable to prepare URI for processing.", false);
1420 return Uri.State.ErrorMessage;
1421 }
1422 }
1423 catch (Exception ex)
1424 {
1425 Uri.State.Error(ex);
1426 return Uri.State.ErrorMessage;
1427 }
1428
1429 try
1430 {
1431 if (await UriTransaction.Execute())
1432 {
1433 if (await UriTransaction.Commit())
1434 return null;
1435
1436 await UriTransaction.Rollback();
1437 }
1438 else
1439 await UriTransaction.Rollback();
1440
1441 Uri.State.Error(EDalerUriErrorType.ResourceConstraint, "Unable to process transaction.", false);
1442 }
1443 catch (Exception ex)
1444 {
1445 await UriTransaction.Abort();
1446
1447 Uri.State.Error(ex);
1448 }
1449
1450 return Uri.State.ErrorMessage;
1451 }
1452
1453 }
1454}
Contains information about a service provider that users can use to buy eDaler.
Result of request payment.
Definition: PaymentResult.cs:7
bool Ok
If payment was successful or not.
string Currency
Currency of amount paid.
decimal Amount
Amount paid.
string Error
Error message, if payment was not successful.
Contains information about a service provider that users can use to sell eDaler.
Contains information about a service provider.
Helps with parsing of commong data types.
Definition: CommonTypes.cs:13
static string Encode(bool x)
Encodes a Boolean for use in XML and other formats.
Definition: CommonTypes.cs:594
static bool TryParse(string s, out double Value)
Tries to decode a string encoded double.
Definition: CommonTypes.cs:46
Helps with common XML-related tasks.
Definition: XML.cs:19
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
Definition: XML.cs:914
static string Encode(string s)
Encodes a string for use in XML.
Definition: XML.cs:27
static bool TryParse(string s, out DateTime Value)
Tries to decode a string encoded DateTime.
Definition: XML.cs:744
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:13
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Definition: Log.cs:1647
static void Error(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an error event.
Definition: Log.cs:682
Static class managing the runtime environment of the IoT Gateway.
Definition: Gateway.cs:126
static DateTime ScheduleEvent(ScheduledEventCallback Callback, DateTime When, object State)
Schedules a one-time event.
Definition: Gateway.cs:3452
Implements an HTTP server.
Definition: HttpServer.cs:36
static Variables CreateVariables()
Creates a new collection of variables, that contains access to the global set of variables.
Definition: HttpServer.cs:1604
Contains information about one XMPP address.
Definition: XmppAddress.cs:9
bool HasAccount
If the address has an account part.
Definition: XmppAddress.cs:167
CaseInsensitiveString Domain
Domain
Definition: XmppAddress.cs:97
CaseInsensitiveString Account
Account
Definition: XmppAddress.cs:124
static bool CheckExpressionSafe(Expression Expression, out ScriptNode Prohibited)
Checks if an expression is safe to execute (if it comes from an external source).
Definition: XmppServer.cs:6733
Represents a case-insensitive string.
string Value
String-representation of the case-insensitive string. (Representation is case sensitive....
static bool IsNullOrEmpty(CaseInsensitiveString value)
Indicates whether the specified string is null or an CaseInsensitiveString.Empty string.
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:19
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:626
static Task< IEnumerable< object > > Find(string Collection, params string[] SortOrder)
Finds objects in a given collection.
Definition: Database.cs:247
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:95
This filter selects objects that have a named field equal to a given value.
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:14
static Type GetType(string FullName)
Gets a type, given its full name.
Definition: Types.cs:41
static object Instantiate(Type Type, params object[] Arguments)
Returns an instance of the type Type . If one needs to be created, it is. If the constructor requires...
Definition: Types.cs:1353
Asynchronous First-in-First-out (FIFO) Queue, for use when transporting items of type T between task...
Definition: AsyncQueue.cs:15
A transaction built up of a set of sub-transactions.
Class managing a script expression.
Definition: Expression.cs:39
async Task< object > EvaluateAsync(Variables Variables)
Evaluates the expression, using the variables provided in the Variables collection....
Definition: Expression.cs:4275
Collection of variables.
Definition: Variables.cs:25
Manages eDaler on accounts connected to the broker.
Abstract base class for eDaler URIs
Definition: EDalerUri.cs:20
abstract void AddTransactionParts(List< ITransaction > Subtransactions, bool LocalOnly, LegalComponent Legal)
Adds subtransaction objects necessary to process the URI.
EDalerUriState State
URI State object.
Definition: EDalerUri.cs:173
static async Task< EDalerUri > Parse(string Uri, EDalerUriState State, EDalerComponent EDaler)
Parses an eDaler URI
Definition: EDalerUri.cs:260
virtual void Error(EDalerUriErrorType ErrorType, string ErrorMessage, bool LogAsNotice)
Reports an error with the URI
string ErrorMessage
Error message, or null if no error.
Marketplace processor, brokering sales of items via tenders and offers defined in smart contracts.
Marketplace processor, brokering sales of items via tenders and offers defined in smart contracts.
Paiwise processor, processing payment instructions defined in smart contracts.
const string PaymentInstructionsNamespace
https://paiwise.tagroot.io/Schema/PaymentInstructions.xsd
const string TrustProviderRole
Role name of Trust Provider.
static byte[] Sign(byte[] Data)
Signs data with the private key of the ledger.
Interface for information about a service provider that users can use to buy eDaler.
string BuyEDalerTemplateContractId
Contract ID of Template, for buying e-Daler
Task< PaymentResult > BuyEDaler(IDictionary< CaseInsensitiveString, object > ContractParameters, IDictionary< CaseInsensitiveString, CaseInsensitiveString > IdentityProperties, decimal Amount, string Currency, string SuccessUrl, string FailureUrl, string CancelUrl, ClientUrlEventHandler ClientUrlCallback, object State)
Processes payment for buying eDaler.
Task< bool > CanBuyEDaler(CaseInsensitiveString AccountName)
If the service provider can be used to process a request to buy eDaler of a certain amount,...
Interface for information about a service provider that users can use to buy eDaler.
Interface for information about a service provider that users can use to sell eDaler.
Task< bool > CanSellEDaler(CaseInsensitiveString AccountName)
If the service provider can be used to process a request to sell eDaler of a certain amount,...
string SellEDalerTemplateContractId
Contract ID of Template, for selling e-Daler
Task< PaymentResult > SellEDaler(IDictionary< CaseInsensitiveString, object > ContractParameters, IDictionary< CaseInsensitiveString, CaseInsensitiveString > IdentityProperties, decimal Amount, string Currency, string SuccessUrl, string FailureUrl, string CancelUrl, ClientUrlEventHandler ClientUrlCallback, object State)
Processes payment for selling eDaler.
Interface for information about a service provider that users can use to sell eDaler.
Grade Supports(T Object)
If the interface understands objects such as Object .
Interface for transactions
Definition: ITransaction.cs:10
Task< bool > Execute()
Executes the transaction.
Task< bool > Prepare()
Prepares the transaction for execution. This step can be used for validation and authorization of the...
Task< bool > Rollback()
Rolls back any changes made during the execution phase.
Task Abort()
Aborts the transaction.
Task< bool > Commit()
Commits any changes made during the execution phase.
delegate Task ClientUrlEventHandler(object Sender, ClientUrlEventArgs e)
Delegat for client URL callback methods.
Grade
Grade enumeration
Definition: Grade.cs:7