2using System.Collections.Generic;
 
    4using System.Text.RegularExpressions;
 
    5using System.Threading.Tasks;
 
   23        private readonly 
string method;
 
   24        private readonly 
string url;
 
   25        private readonly 
string optionsUrl;
 
   26        private readonly 
string host;
 
   44            int IconHeight, 
string TemplateContractId, 
string Method, 
string Url, 
string OptionsUrl,
 
   50            this.optionsUrl = OptionsUrl;
 
   54            this.SellEDalerTemplateContractId = TemplateContractId;
 
   55            this.SellEDalerServiceProvider = Provider;
 
   76                Currency.Length == 3 ? Grade.Ok : 
Grade.NotAtAll;
 
   87            if (
string.Compare(this.method, 
"POST", 
true) != 0)
 
   92            return !
string.IsNullOrEmpty(Token);
 
   95        private static readonly Regex PaiwiseProcessorRegex = 
new Regex(
@"Waher\.Service\.IoTBroker\.Paiwise\.PaiwiseProcessor[.+]<(SellEDaler|SellEDaler)>\w*([.]\w*)?",
 
   96            RegexOptions.Compiled | RegexOptions.Singleline);
 
   98        private static readonly Regex UnitTestRegex = 
new Regex(
@"Paiwise\.Internal\.Test\.PaiwiseTests[.+]<(Test_07_ProcessSellEDalerUsingContract|Test_08_ProcessSellEDalerUsingClientUrl)>\w*([.]\w*)?",
 
   99            RegexOptions.Compiled | RegexOptions.Singleline);
 
  101        private static readonly 
object[] approvedSources = 
new object[]
 
  103            PaiwiseProcessorRegex,
 
  123        public async Task<PaymentResult> 
SellEDaler(IDictionary<CaseInsensitiveString, object> ContractParameters,
 
  124            IDictionary<CaseInsensitiveString, CaseInsensitiveString> IdentityProperties,
 
  125            decimal Amount, 
string Currency, 
string SuccessUrl, 
string FailureUrl, 
string CancelUrl, 
ClientUrlEventHandler ClientUrlCallback, 
object State)
 
  132                if (
string.IsNullOrEmpty(Currency) ||
 
  133                    Currency.Length != 3 ||
 
  134                    Currency.ToUpper() != Currency)
 
  140                Dictionary<string, object> Request = 
new Dictionary<string, object>()
 
  142                    { 
"webhook", string.Empty }
 
  147                    if (ContractParameters.TryGetValue(Field.
FieldId, out Obj))
 
  150                        Request[Field.
FieldId] = Obj2.Value;
 
  156                                Request[Field.
FieldId] = SuccessUrl;
 
  159                            case "returnerrorurl":
 
  160                                Request[Field.
FieldId] = FailureUrl;
 
  164                                Request[Field.
FieldId] = CancelUrl;
 
  168                                Request[Field.
FieldId] = Amount;
 
  172                                Request[Field.
FieldId] = Currency;
 
  176                                StringBuilder sb = 
new StringBuilder();
 
  179                                Append(sb, 
"FIRST", IdentityProperties, ref First);
 
  180                                Append(sb, 
"MIDDLE", IdentityProperties, ref First);
 
  181                                Append(sb, 
"LAST", IdentityProperties, ref First);
 
  184                                    Append(sb, 
"PNR", IdentityProperties, ref First);
 
  186                                Request[Field.
FieldId] = sb.ToString();
 
  190                                sb = 
new StringBuilder();
 
  193                                Append(sb, 
"ADDR", IdentityProperties, ref First);
 
  195                                Request[Field.
FieldId] = sb.ToString();
 
  200                                    Request[Field.
FieldId] = 
string.Empty;
 
  209                if (
string.IsNullOrEmpty(Token))
 
  217                        new KeyValuePair<string, string>(
"Authorization", 
"Bearer " + Token),
 
  222                    Log.
Error(
"Sending paiwise request to " + this.url + 
", but an error was returned:" + ex.Message + 
 
  223                        "\r\n\r\n" + 
JSON.
Encode(Request, 
true), 
new KeyValuePair<string, object>(
"Token", 
"Token"));    
 
  228                if (!(Result is Dictionary<string, object> Response) ||
 
  229                    !Response.TryGetValue(
"status", out Obj) || !(Obj is 
string Status))
 
  231                    return new PaymentResult(
"Invalid response returned from Paiwise");
 
  234                if (Status == 
"paid")
 
  236                    if (Response.TryGetValue(
"amount", out Obj) && IsDecimal(Obj, out decimal Amount2))
 
  239                    if (Response.TryGetValue(
"currency", out Obj) && Obj is 
string Currency2)
 
  240                        Currency = Currency2;
 
  245                if (
int.TryParse(Status, out _) &&
 
  246                    Response.TryGetValue(
"message", out Obj) && Obj is 
string Message)
 
  251                if (Status == 
"pending")        
 
  253                    if (!Response.TryGetValue(
"id", out Obj) || !(Obj is 
string TransactionId))
 
  256                    if (Response.TryGetValue(
"redirectUrl", out Obj) && Obj is 
string RedirectUrl)
 
  258                        if (ClientUrlCallback is 
null)
 
  259                            return new PaymentResult(
"No Client URL callback method defined.");
 
  273                    StringBuilder Url = 
new StringBuilder();
 
  275                    Url.Append(
"https://");
 
  276                    Url.Append(this.host);
 
  277                    Url.Append(
"/payment/retrieve");
 
  279                    Request = 
new Dictionary<string, object>()
 
  281                        { 
"id", TransactionId }
 
  284                    DateTime Start = DateTime.Now;
 
  286                    while (Status == 
"pending" && DateTime.Now.Subtract(Start).TotalMinutes < TimeoutMinutes)
 
  288                        await Task.Delay(2000);
 
  291                            new KeyValuePair<string, string>(
"Authorization", 
"Bearer " + Token),
 
  294                        if (!(Obj is Dictionary<string, object> Response2) ||
 
  295                            !Response2.TryGetValue(
"status", out Obj) || !(Obj is 
string Status2))
 
  297                            return new PaymentResult(
"Invalid polling response returned from Paiwise");
 
  300                        if (
int.TryParse(Status2, out _) &&
 
  301                            Response2.TryGetValue(
"message", out Obj) && Obj is 
string Message2)
 
  308                        if (Status == 
"paid" &&
 
  309                            Response2.TryGetValue(
"request", out Obj) &&
 
  310                            Obj is Dictionary<string, object> Request2)
 
  312                            if (Request2.TryGetValue(
"amount", out Obj) && IsDecimal(Obj, out decimal Amount2))
 
  315                            if (Request2.TryGetValue(
"currency", out Obj) && Obj is 
string Currency2)
 
  316                                Currency = Currency2;
 
  320                    if (Status == 
"pending")
 
  322                        await Task.Delay(2000);
 
  325                        Url.Append(
"https://");
 
  326                        Url.Append(this.host);
 
  327                        Url.Append(
"/payment/cancel");
 
  330                            new KeyValuePair<string, string>(
"Authorization", 
"Bearer " + Token),
 
  333                        if (!(Obj is Dictionary<string, object> Response2) ||
 
  334                            !Response2.TryGetValue(
"status", out Obj) || !(Obj is 
string Status2))
 
  336                            return new PaymentResult(
"Invalid cancel response returned from Paiwise");
 
  339                        if (
int.TryParse(Status2, out _) &&
 
  340                            Response2.TryGetValue(
"message", out Obj) && Obj is 
string Message2)
 
  349                if (Status == 
"paid")
 
  351                else if (Status == 
"cancelled")
 
  363            IDictionary<CaseInsensitiveString, CaseInsensitiveString> IdentityProperties, ref 
bool First)
 
  376        private static bool IsDecimal(
object Obj, out decimal Result)
 
  383            else if (Obj is decimal d)
 
  388            else if (Obj is 
double d2)
 
  390                Result = (decimal)d2;
 
  413            IDictionary<CaseInsensitiveString, CaseInsensitiveString> IdentityProperties,
 
  414            string SuccessUrl, 
string FailureUrl, 
string CancelUrl,
 
  419                if (
string.IsNullOrEmpty(this.optionsUrl))
 
  420                    return new IDictionary<CaseInsensitiveString, object>[0];
 
  422                Dictionary<string, object> Request = 
new Dictionary<string, object>()
 
  424                    { 
"webhook", string.Empty }
 
  430                        Request[Field.
FieldId] = Obj2.Value;
 
  436                                Request[Field.
FieldId] = SuccessUrl ?? 
string.Empty;
 
  439                            case "returnerrorurl":
 
  440                                Request[Field.
FieldId] = FailureUrl ?? 
string.Empty;
 
  444                                Request[Field.
FieldId] = CancelUrl ?? 
string.Empty;
 
  448                                StringBuilder sb = 
new StringBuilder();
 
  451                                Append(sb, 
"FIRST", IdentityProperties, ref First);
 
  452                                Append(sb, 
"MIDDLE", IdentityProperties, ref First);
 
  453                                Append(sb, 
"LAST", IdentityProperties, ref First);
 
  456                                    Append(sb, 
"PNR", IdentityProperties, ref First);
 
  458                                Request[Field.
FieldId] = sb.ToString();
 
  462                                sb = 
new StringBuilder();
 
  465                                Append(sb, 
"ADDR", IdentityProperties, ref First);
 
  467                                Request[Field.
FieldId] = sb.ToString();
 
  478                if (
string.IsNullOrEmpty(Token))
 
  479                    return new IDictionary<CaseInsensitiveString, object>[0];
 
  486                        new KeyValuePair<string, string>(
"Authorization", 
"Bearer " + Token),
 
  491                    Log.
Error(
"Requesting payment options for paiwise request to " + this.optionsUrl + 
", but an error was returned:" + ex.Message +
 
  492                        "\r\n\r\n" + 
JSON.
Encode(Request, 
true), 
new KeyValuePair<string, object>(
"Token", 
"Token"));    
 
  494                    return new IDictionary<CaseInsensitiveString, object>[0];
 
  497                if (!(Result is Array A))
 
  498                    throw new Exception(
"Unexpected response type returned: " + Result.GetType().FullName);
 
  500                List<IDictionary<CaseInsensitiveString, object>> Options = 
new List<IDictionary<CaseInsensitiveString, object>>();
 
  502                foreach (
object Item 
in A)
 
  504                    if (Item is Dictionary<string, object> Option)
 
  506                        Dictionary<CaseInsensitiveString, object> Properties = 
new Dictionary<CaseInsensitiveString, object>();
 
  508                        foreach (KeyValuePair<string, object> P 
in Option)
 
  509                            Properties[P.Key] = P.Value;
 
  511                        Options.Add(Properties);
 
  515                return Options.ToArray();
 
  521                    new KeyValuePair<string, object>(
"ServiceId", 
this.Id),
 
  522                    new KeyValuePair<string, object>(
"URL", 
this.optionsUrl));
 
  524                return new IDictionary<CaseInsensitiveString, object>[0];
 
Event arguments for callback methods with the aim of pushing a URL to a client.
Payment services made available by Paiwise.
const string TimeoutMinutesSetting
Settings key for Paiwise timeout, in minutes.
const string TokenIdSetting
Settings key for Paiwise token.
Reference to a Paiwise payment service.
SellEDalerPaymentService(string Id, string Name, string IconUrl, int IconWidth, int IconHeight, string TemplateContractId, string Method, string Url, string OptionsUrl, ServiceField[] Fields, string Host, ISellEDalerServiceProvider Provider)
Reference to a Paiwise payment service.
string SellEDalerTemplateContractId
Optional Contract ID of Template, for selling e-Daler
async Task< bool > CanSellEDaler(CaseInsensitiveString AccountName)
If the service provider can be used to process a request to sell eDaler of a certain amount,...
async 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.
Grade Supports(CaseInsensitiveString Currency)
Checks if the service provider supports a given currency.
async Task< IDictionary< CaseInsensitiveString, object >[]> GetPaymentOptionsForSellingEDaler(IDictionary< CaseInsensitiveString, CaseInsensitiveString > IdentityProperties, string SuccessUrl, string FailureUrl, string CancelUrl, ClientUrlEventHandler ClientUrlCallback, object State)
Gets available payment options for selling eDaler.
Represents a field in a service.
bool Required
If the field is required or not.
CaseInsensitiveString FieldId
Field ID
Result of request payment.
Contains information about a service provider that users can use to sell eDaler.
Contains information about a service provider.
string IconUrl
Optional URL to icon of service provider.
string Id
ID of service provider.
int IconHeight
Height of icon, if available.
int IconWidth
Width of icon, if available.
string Name
Displayable name of service provider.
Static class managing encoding and decoding of internet content.
static Task< object > PostAsync(Uri Uri, object Data, params KeyValuePair< string, string >[] Headers)
Posts to a resource, using a Uniform Resource Identifier (or Locator).
Helps with common JSON-related tasks.
static string Encode(string s)
Encodes a string for inclusion in JSON.
const string DefaultContentType
application/json
Static class managing the application event log. Applications and services log events on this static ...
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.
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.
Static class managing the runtime environment of the IoT Gateway.
static X509Certificate2 Certificate
Domain certificate.
Represents a case-insensitive string.
string LowerCase
Lower-case representation of the case-insensitive string.
static bool IsNullOrEmpty(CaseInsensitiveString value)
Indicates whether the specified string is null or an CaseInsensitiveString.Empty string.
Static class managing persistent settings.
static async Task< string > GetAsync(string Key, string DefaultValue)
Gets a string-valued setting.
Interface for information about a service provider that users can use to sell eDaler.
Interface for information about a service provider that users can use to sell eDaler.
delegate Task ClientUrlEventHandler(object Sender, ClientUrlEventArgs e)
Delegat for client URL callback methods.