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