4using System.Threading.Tasks;
21 private readonly
string method;
22 private readonly
string url;
23 private readonly
string optionsUrl;
24 private readonly
string host;
42 int IconHeight,
string TemplateContractId,
string Method,
string Url,
string OptionsUrl,
48 this.optionsUrl = OptionsUrl;
52 this.BuyEDalerTemplateContractId = TemplateContractId;
53 this.BuyEDalerServiceProvider = Provider;
54 this.PaymentServiceProvider = Provider;
75 Currency.Length == 3 ? Grade.Ok :
Grade.NotAtAll;
86 if (
string.Compare(this.method,
"POST",
true) != 0)
91 return !
string.IsNullOrEmpty(Token);
110 public async Task<PaymentResult>
BuyEDaler(IDictionary<CaseInsensitiveString, object> ContractParameters,
111 IDictionary<CaseInsensitiveString, CaseInsensitiveString> IdentityProperties,
112 decimal Amount,
string Currency,
string SuccessUrl,
string FailureUrl,
string CancelUrl,
113 EventHandlerAsync<ClientUrlEventArgs> ClientUrlCallback,
object State)
120 if (
string.IsNullOrEmpty(Currency) ||
121 Currency.Length != 3 ||
122 Currency.ToUpper() != Currency)
127 Dictionary<string, object> Request =
new Dictionary<string, object>()
129 {
"webhook", string.Empty }
134 if (ContractParameters.TryGetValue(Field.
FieldId, out
object Obj))
137 Request[Field.
FieldId] = Obj2.Value;
143 Request[Field.
FieldId] = SuccessUrl ??
string.Empty;
146 case "returnerrorurl":
147 Request[Field.
FieldId] = FailureUrl ??
string.Empty;
151 Request[Field.
FieldId] = CancelUrl ??
string.Empty;
155 Request[Field.
FieldId] = Amount;
159 Request[Field.
FieldId] = Currency;
170 return await this.ProcessRequest(Request, Amount, Currency, ClientUrlCallback, State);
178 private async Task<PaymentResult> ProcessRequest(Dictionary<string, object> Request, decimal Amount,
string Currency,
179 EventHandlerAsync<ClientUrlEventArgs> ClientUrlCallback,
object State)
184 if (
string.IsNullOrEmpty(Token))
188 new KeyValuePair<string, string>(
"Authorization",
"Bearer " + Token),
193 Log.
Error(
"Sending paiwise request to " + this.url +
", but an error was returned:" + Result.
Error.Message);
198 if (!(Result.
Decoded is Dictionary<string, object> Response) ||
199 !Response.TryGetValue(
"status", out
object Obj) || !(Obj is
string Status))
201 return new PaymentResult(
"Invalid response returned from Paiwise");
204 if (Status ==
"paid")
206 if (Response.TryGetValue(
"amount", out Obj) && IsDecimal(Obj, out decimal Amount2))
209 if (Response.TryGetValue(
"currency", out Obj) && Obj is
string Currency2)
210 Currency = Currency2;
212 return new PaymentResult(Amount, Currency);
215 if (
int.TryParse(Status, out
_) &&
216 Response.TryGetValue(
"message", out Obj) && Obj is
string Message)
218 return new PaymentResult(Message);
221 if (Status ==
"pending")
223 if (!Response.TryGetValue(
"id", out Obj) || !(Obj is
string TransactionId))
224 return new PaymentResult(
"No transaction ID returned.");
226 if (Response.TryGetValue(
"redirectUrl", out Obj) && Obj is
string RedirectUrl)
228 if (ClientUrlCallback is
null)
229 return new PaymentResult(
"No Client URL callback method defined.");
231 await ClientUrlCallback.Raise(
this,
new ClientUrlEventArgs(RedirectUrl, State));
236 StringBuilder Url =
new StringBuilder();
238 Url.Append(
"https://");
239 Url.Append(this.host);
240 Url.Append(
"/payment/retrieve");
242 Request =
new Dictionary<string, object>()
244 {
"id", TransactionId }
247 DateTime Start = DateTime.Now;
249 while (Status ==
"pending" && DateTime.Now.Subtract(Start).TotalMinutes < TimeoutMinutes)
251 await Task.Delay(2000);
254 new KeyValuePair<string, string>(
"Authorization",
"Bearer " + Token),
258 return new PaymentResult(Result.
Error.Message);
260 if (!(Result.
Decoded is Dictionary<string, object> Response2) ||
261 !Response2.TryGetValue(
"status", out Obj) || !(Obj is
string Status2))
263 return new PaymentResult(
"Invalid polling response returned from Paiwise");
266 if (
int.TryParse(Status2, out
_) &&
267 Response2.TryGetValue(
"message", out Obj) && Obj is
string Message2)
269 return new PaymentResult(Message2);
274 if (Status ==
"paid" &&
275 Response2.TryGetValue(
"request", out Obj) &&
276 Obj is Dictionary<string, object> Request2)
278 if (Request2.TryGetValue(
"amount", out Obj) && IsDecimal(Obj, out decimal Amount2))
281 if (Request2.TryGetValue(
"currency", out Obj) && Obj is
string Currency2)
282 Currency = Currency2;
286 if (Status ==
"pending")
288 await Task.Delay(2000);
291 Url.Append(
"https://");
292 Url.Append(this.host);
293 Url.Append(
"/payment/cancel");
296 new KeyValuePair<string, string>(
"Authorization",
"Bearer " + Token),
300 return new PaymentResult(Result.
Error.Message);
302 if (!(Result.
Decoded is Dictionary<string, object> Response2) ||
303 !Response2.TryGetValue(
"status", out Obj) || !(Obj is
string Status2))
305 return new PaymentResult(
"Invalid cancel response returned from Paiwise");
308 if (
int.TryParse(Status2, out
_) &&
309 Response2.TryGetValue(
"message", out Obj) && Obj is
string Message2)
311 return new PaymentResult(Message2);
318 if (Status ==
"paid")
319 return new PaymentResult(Amount, Currency);
320 else if (Status ==
"cancelled")
321 return new PaymentResult(
"Payment has been cancelled.");
323 return new PaymentResult(Status);
326 private static bool IsDecimal(
object Obj, out decimal Result)
333 else if (Obj is decimal d)
338 else if (Obj is
double d2)
340 Result = (decimal)d2;
363 IDictionary<CaseInsensitiveString, CaseInsensitiveString> IdentityProperties,
364 string SuccessUrl,
string FailureUrl,
string CancelUrl,
365 EventHandlerAsync<ClientUrlEventArgs> ClientUrlCallback,
object State)
369 if (
string.IsNullOrEmpty(this.optionsUrl))
370 return Array.Empty<IDictionary<CaseInsensitiveString, object>>();
372 Dictionary<string, object> Request =
new Dictionary<string, object>()
374 {
"webhook", string.Empty }
380 Request[Field.
FieldId] = Obj2.Value;
383 Request[Field.
FieldId] = Field.FieldId.LowerCase
switch
385 "returnurl" => SuccessUrl ??
string.Empty,
386 "returnerrorurl" => FailureUrl ??
string.Empty,
387 "cancelurl" => CancelUrl ??
string.Empty,
394 if (
string.IsNullOrEmpty(Token))
395 return Array.Empty<IDictionary<CaseInsensitiveString, object>>();
398 new KeyValuePair<string, string>(
"Authorization",
"Bearer " + Token),
403 Log.
Error(
"Requesting payment options for paiwise request to " + this.optionsUrl +
", but an error was returned:" + Result.Error.Message);
404 return Array.Empty<IDictionary<CaseInsensitiveString, object>>();
407 if (!(Result.Decoded is Array A))
408 throw new Exception(
"Unexpected response type returned: " + Result.GetType().FullName);
410 List<IDictionary<CaseInsensitiveString, object>> Options =
new List<IDictionary<CaseInsensitiveString, object>>();
412 foreach (
object Item
in A)
414 if (Item is Dictionary<string, object> Option)
416 Dictionary<CaseInsensitiveString, object> Properties =
new Dictionary<CaseInsensitiveString, object>();
418 foreach (KeyValuePair<string, object> P
in Option)
419 Properties[P.Key] = P.Value;
421 Options.Add(Properties);
425 return Options.ToArray();
431 new KeyValuePair<string, object>(
"ServiceId",
this.Id),
432 new KeyValuePair<string, object>(
"URL",
this.optionsUrl));
434 return Array.Empty<IDictionary<CaseInsensitiveString, object>>();
453 case "returnerrorurl":
487 public async Task<PaymentResult>
Pay(decimal Amount,
string Currency,
string Description,
string SuccessUrl,
string FailureUrl,
string CancelUrl,
488 EventHandlerAsync<ClientUrlEventArgs> ClientUrlCallback,
object State)
495 if (
string.IsNullOrEmpty(Currency) ||
496 Currency.Length != 3 ||
497 Currency.ToUpper() != Currency)
502 Dictionary<string, object> Request =
new Dictionary<string, object>()
504 {
"webhook", string.Empty }
512 Request[Field.
FieldId] = SuccessUrl ??
string.Empty;
515 case "returnerrorurl":
516 Request[Field.
FieldId] = FailureUrl ??
string.Empty;
520 Request[Field.
FieldId] = CancelUrl ??
string.Empty;
524 Request[Field.
FieldId] = Amount;
528 Request[Field.
FieldId] = Currency;
538 return await this.ProcessRequest(Request, Amount, Currency, ClientUrlCallback, State);
Contains information about a service provider that users can use to buy eDaler.
Reference to a Paiwise payment service.
string BuyEDalerTemplateContractId
Optional Contract ID of Template, for buying e-Daler
async Task< PaymentResult > Pay(decimal Amount, string Currency, string Description, string SuccessUrl, string FailureUrl, string CancelUrl, EventHandlerAsync< ClientUrlEventArgs > ClientUrlCallback, object State)
Processes a payment.
Grade Supports(CaseInsensitiveString Currency)
Checks if the service provider supports a given currency.
async 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.
IPaymentServiceProvider PaymentServiceProvider
Reference to service provider.
async Task< bool > CanBuyEDaler(CaseInsensitiveString AccountName)
If the service provider can be used to process a request to buy eDaler of a certain amount,...
bool CanBeUsedForPayment
If service can be used for payments.
async Task< IDictionary< CaseInsensitiveString, object >[]> GetPaymentOptionsForBuyingEDaler(IDictionary< CaseInsensitiveString, CaseInsensitiveString > IdentityProperties, string SuccessUrl, string FailureUrl, string CancelUrl, EventHandlerAsync< ClientUrlEventArgs > ClientUrlCallback, object State)
Gets available payment options for buying eDaler.
BuyEDalerPaymentService(string Id, string Name, string IconUrl, int IconWidth, int IconHeight, string TemplateContractId, string Method, string Url, string OptionsUrl, ServiceField[] Fields, string Host, PaiwisePaymentServices Provider)
Reference to a Paiwise payment service.
Payment services made available by Paiwise.
const string TokenIdSetting
Settings key for Paiwise token.
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.
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.
Contains information about a response to a content request.
bool HasError
If an error occurred.
object Decoded
Decoded object.
Exception Error
Error response.
Static class managing encoding and decoding of internet content.
static Task< ContentResponse > PostAsync(Uri Uri, object Data, params KeyValuePair< string, string >[] Headers)
Posts to a resource, using a Uniform Resource Identifier (or Locator).
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 buy eDaler.
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 pay for services.
Interface for information about a service provider that users can use to pay for services.