2using System.Collections.Generic;
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,
ClientUrlEventHandler ClientUrlCallback,
object State)
119 if (
string.IsNullOrEmpty(Currency) ||
120 Currency.Length != 3 ||
121 Currency.ToUpper() != Currency)
126 Dictionary<string, object> Request =
new Dictionary<string, object>()
128 {
"webhook", string.Empty }
133 if (ContractParameters.TryGetValue(Field.
FieldId, out
object Obj))
136 Request[Field.
FieldId] = Obj2.Value;
142 Request[Field.
FieldId] = SuccessUrl ??
string.Empty;
145 case "returnerrorurl":
146 Request[Field.
FieldId] = FailureUrl ??
string.Empty;
150 Request[Field.
FieldId] = CancelUrl ??
string.Empty;
154 Request[Field.
FieldId] = Amount;
158 Request[Field.
FieldId] = Currency;
169 return await this.ProcessRequest(Request, Amount, Currency, ClientUrlCallback, State);
177 private async Task<PaymentResult> ProcessRequest(Dictionary<string, object> Request, decimal Amount,
string Currency,
183 if (
string.IsNullOrEmpty(Token))
191 new KeyValuePair<string, string>(
"Authorization",
"Bearer " + Token),
196 Log.
Error(
"Sending paiwise request to " + this.url +
", but an error was returned:" + ex.Message +
197 "\r\n\r\n" +
JSON.
Encode(Request,
true),
new KeyValuePair<string, object>(
"Token",
"Token"));
202 if (!(Result is Dictionary<string, object> Response) ||
203 !Response.TryGetValue(
"status", out
object Obj) || !(Obj is
string Status))
205 return new PaymentResult(
"Invalid response returned from Paiwise");
208 if (Status ==
"paid")
210 if (Response.TryGetValue(
"amount", out Obj) && IsDecimal(Obj, out decimal Amount2))
213 if (Response.TryGetValue(
"currency", out Obj) && Obj is
string Currency2)
214 Currency = Currency2;
216 return new PaymentResult(Amount, Currency);
219 if (
int.TryParse(Status, out _) &&
220 Response.TryGetValue(
"message", out Obj) && Obj is
string Message)
222 return new PaymentResult(Message);
225 if (Status ==
"pending")
227 if (!Response.TryGetValue(
"id", out Obj) || !(Obj is
string TransactionId))
228 return new PaymentResult(
"No transaction ID returned.");
230 if (Response.TryGetValue(
"redirectUrl", out Obj) && Obj is
string RedirectUrl)
232 if (ClientUrlCallback is
null)
233 return new PaymentResult(
"No Client URL callback method defined.");
237 await ClientUrlCallback(
this,
new ClientUrlEventArgs(RedirectUrl, State));
241 return new PaymentResult(ex.Message);
247 StringBuilder Url =
new StringBuilder();
249 Url.Append(
"https://");
250 Url.Append(this.host);
251 Url.Append(
"/payment/retrieve");
253 Request =
new Dictionary<string, object>()
255 {
"id", TransactionId }
258 DateTime Start = DateTime.Now;
260 while (Status ==
"pending" && DateTime.Now.Subtract(Start).TotalMinutes < TimeoutMinutes)
262 await Task.Delay(2000);
265 new KeyValuePair<string, string>(
"Authorization",
"Bearer " + Token),
268 if (!(Obj is Dictionary<string, object> Response2) ||
269 !Response2.TryGetValue(
"status", out Obj) || !(Obj is
string Status2))
271 return new PaymentResult(
"Invalid polling response returned from Paiwise");
274 if (
int.TryParse(Status2, out _) &&
275 Response2.TryGetValue(
"message", out Obj) && Obj is
string Message2)
277 return new PaymentResult(Message2);
282 if (Status ==
"paid" &&
283 Response2.TryGetValue(
"request", out Obj) &&
284 Obj is Dictionary<string, object> Request2)
286 if (Request2.TryGetValue(
"amount", out Obj) && IsDecimal(Obj, out decimal Amount2))
289 if (Request2.TryGetValue(
"currency", out Obj) && Obj is
string Currency2)
290 Currency = Currency2;
294 if (Status ==
"pending")
296 await Task.Delay(2000);
299 Url.Append(
"https://");
300 Url.Append(this.host);
301 Url.Append(
"/payment/cancel");
304 new KeyValuePair<string, string>(
"Authorization",
"Bearer " + Token),
307 if (!(Obj is Dictionary<string, object> Response2) ||
308 !Response2.TryGetValue(
"status", out Obj) || !(Obj is
string Status2))
310 return new PaymentResult(
"Invalid cancel response returned from Paiwise");
313 if (
int.TryParse(Status2, out _) &&
314 Response2.TryGetValue(
"message", out Obj) && Obj is
string Message2)
316 return new PaymentResult(Message2);
323 if (Status ==
"paid")
324 return new PaymentResult(Amount, Currency);
325 else if (Status ==
"cancelled")
326 return new PaymentResult(
"Payment has been cancelled.");
328 return new PaymentResult(Status);
331 private static bool IsDecimal(
object Obj, out decimal Result)
338 else if (Obj is decimal d)
343 else if (Obj is
double d2)
345 Result = (decimal)d2;
368 IDictionary<CaseInsensitiveString, CaseInsensitiveString> IdentityProperties,
369 string SuccessUrl,
string FailureUrl,
string CancelUrl,
374 if (
string.IsNullOrEmpty(this.optionsUrl))
375 return new IDictionary<CaseInsensitiveString, object>[0];
377 Dictionary<string, object> Request =
new Dictionary<string, object>()
379 {
"webhook", string.Empty }
385 Request[Field.
FieldId] = Obj2.Value;
391 Request[Field.
FieldId] = SuccessUrl ??
string.Empty;
394 case "returnerrorurl":
395 Request[Field.
FieldId] = FailureUrl ??
string.Empty;
399 Request[Field.
FieldId] = CancelUrl ??
string.Empty;
410 if (
string.IsNullOrEmpty(Token))
411 return new IDictionary<CaseInsensitiveString, object>[0];
418 new KeyValuePair<string, string>(
"Authorization",
"Bearer " + Token),
423 Log.
Error(
"Requesting payment options for paiwise request to " + this.optionsUrl +
", but an error was returned:" + ex.Message +
424 "\r\n\r\n" +
JSON.
Encode(Request,
true),
new KeyValuePair<string, object>(
"Token",
"Token"));
426 return new IDictionary<CaseInsensitiveString, object>[0];
429 if (!(Result is Array A))
430 throw new Exception(
"Unexpected response type returned: " + Result.GetType().FullName);
432 List<IDictionary<CaseInsensitiveString, object>> Options =
new List<IDictionary<CaseInsensitiveString, object>>();
434 foreach (
object Item
in A)
436 if (Item is Dictionary<string, object> Option)
438 Dictionary<CaseInsensitiveString, object> Properties =
new Dictionary<CaseInsensitiveString, object>();
440 foreach (KeyValuePair<string, object> P
in Option)
441 Properties[P.Key] = P.Value;
443 Options.Add(Properties);
447 return Options.ToArray();
453 new KeyValuePair<string, object>(
"ServiceId",
this.Id),
454 new KeyValuePair<string, object>(
"URL",
this.optionsUrl));
456 return new IDictionary<CaseInsensitiveString, object>[0];
475 case "returnerrorurl":
509 public async Task<PaymentResult>
Pay(decimal Amount,
string Currency,
string Description,
string SuccessUrl,
string FailureUrl,
string CancelUrl,
517 if (
string.IsNullOrEmpty(Currency) ||
518 Currency.Length != 3 ||
519 Currency.ToUpper() != Currency)
524 Dictionary<string, object> Request =
new Dictionary<string, object>()
526 {
"webhook", string.Empty }
534 Request[Field.
FieldId] = SuccessUrl ??
string.Empty;
537 case "returnerrorurl":
538 Request[Field.
FieldId] = FailureUrl ??
string.Empty;
542 Request[Field.
FieldId] = CancelUrl ??
string.Empty;
546 Request[Field.
FieldId] = Amount;
550 Request[Field.
FieldId] = Currency;
560 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
Grade Supports(CaseInsensitiveString Currency)
Checks if the service provider supports a given currency.
async Task< IDictionary< CaseInsensitiveString, object >[]> GetPaymentOptionsForBuyingEDaler(IDictionary< CaseInsensitiveString, CaseInsensitiveString > IdentityProperties, string SuccessUrl, string FailureUrl, string CancelUrl, ClientUrlEventHandler ClientUrlCallback, object State)
Gets available payment options 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,...
async Task< PaymentResult > Pay(decimal Amount, string Currency, string Description, string SuccessUrl, string FailureUrl, string CancelUrl, ClientUrlEventHandler ClientUrlCallback, object State)
Processes a payment.
bool CanBeUsedForPayment
If service can be used for payments.
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.
async 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.
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.
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 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.
delegate Task ClientUrlEventHandler(object Sender, ClientUrlEventArgs e)
Delegat for client URL callback methods.